package middleware

import (
	"go-mp-starter/app/common/response"
	"go-mp-starter/app/services"
	"go-mp-starter/global"
	"time"

	"github.com/dgrijalva/jwt-go"
	"github.com/gin-gonic/gin"
)

// 解析token并判断过期
func getTokenFromJwt(tokenStr string) (*jwt.Token, error) {
	return jwt.ParseWithClaims(tokenStr, &services.CustomClaims{}, func(t *jwt.Token) (interface{}, error) {
		return []byte(global.App.Config.Jwt.Secret), nil
	})
}

// 解析token允许过期
func getTokenUnverified(tokenStr string) (*jwt.Token, error) {
	token, _, err := new(jwt.Parser).ParseUnverified(tokenStr, &services.CustomClaims{})
	return token, err
}

// 鉴权方法
func jWTAuthBase(GuardName string, parseTokenFunc func(string) (*jwt.Token, error)) gin.HandlerFunc {
	return func(ctx *gin.Context) {
		tokenStr := ctx.Request.Header.Get("Authorization")
		if tokenStr == "" {
			response.TokenFail(ctx)
			ctx.Abort()
			return
		}
		tokenStr = tokenStr[len(services.TokenType)+1:]

		// 解析token
		token, err := parseTokenFunc(tokenStr)

		// 判断token是否在黑名单中
		if err != nil || services.JwtService.IsInBlackList(tokenStr) {
			response.TokenFail(ctx)
			ctx.Abort()
			return
		}
		// 断言自定义claims
		claims := token.Claims.(*services.CustomClaims)
		// token发布者校验
		if claims.Issuer != GuardName {
			response.TokenFail(ctx)
			ctx.Abort()
			return
		}
		// 判断是否已经过可刷新时间
		if time.Now().Unix() > claims.ExpiresAt+int64(global.App.Config.Jwt.RefreshGracePeriod) {
			response.TokenFail(ctx)
			ctx.Abort()
			return
		}

		// 将数据绑定到gin.Context
		ctx.Set("token", token)
		ctx.Set("id", claims.Id)
	}
}

// JWTAuth 中间件JWT鉴权
func JWTAuth(GuardName string) gin.HandlerFunc {
	return jWTAuthBase(GuardName, getTokenFromJwt)
}

// JWTAuthUnverify 解析token允许过期
func JWTAuthUnverify(GuardName string) gin.HandlerFunc {
	return jWTAuthBase(GuardName, getTokenUnverified)
}
